﻿#pragma once

namespace Hazal
{
	enum class EShaderDataType
	{
		None = 0,
		Float, Float2, Float3, Float4,

		Mat3, Mat4,

		Int, Int2, Int3, Int4,

		Bool,
	};

	static uint32_t ShaderDataTypeSize(EShaderDataType type)
	{
		switch (type)
		{
		case EShaderDataType::None:     return 0;
		case EShaderDataType::Float:    return 4;
		case EShaderDataType::Float2:   return 4 * 2;
		case EShaderDataType::Float3:   return 4 * 3;
		case EShaderDataType::Float4:   return 4 * 4;
		case EShaderDataType::Mat3:		return 4 * 3 * 3;
		case EShaderDataType::Mat4:		return 4 * 4 * 4;
		case EShaderDataType::Int:		return 4;
		case EShaderDataType::Int2:		return 4 * 2;
		case EShaderDataType::Int3:		return 4 * 3;
		case EShaderDataType::Int4:		return 4 * 4;
		case EShaderDataType::Bool:		return 1;
		}

		HZ_ASSERT(false, "Unknown ShaderDataType!")
		return 0;
	}

	/// <summary>
	/// 顶点属性结构体
	/// </summary>
	struct SBufferElement
	{
		SBufferElement(EShaderDataType type, const std::string& strName, bool bNormalized = false)
			:m_eType(type), m_strName(strName), m_unSize(ShaderDataTypeSize(type)), m_unOffset(0), m_bNormalized(bNormalized)
		{

		}

		uint32_t GetComponentCount() const
		{
			switch (m_eType)
			{
			case EShaderDataType::None:     return 0;
			case EShaderDataType::Float:    return 1;
			case EShaderDataType::Float2:   return 2;
			case EShaderDataType::Float3:   return 3;
			case EShaderDataType::Float4:   return 4;
			case EShaderDataType::Mat3:		return 3 * 3;
			case EShaderDataType::Mat4:		return 4 * 4;
			case EShaderDataType::Int:		return 1;
			case EShaderDataType::Int2:		return 2;
			case EShaderDataType::Int3:		return 3;
			case EShaderDataType::Int4:		return 4;
			case EShaderDataType::Bool:		return 1;
			}

			HZ_ASSERT(false, "Unknown ShaderDataType!")
			return 0;
		}

		std::string m_strName;
		EShaderDataType m_eType;
		uint32_t m_unSize;
		uint32_t m_unOffset;
		bool m_bNormalized;
	};

	/// <summary>
	/// 顶点属性布局
	/// </summary>
	class CBufferLayout
	{
		using BufferElementVec = std::vector<SBufferElement>;

	public:
		CBufferLayout() : m_unStride(0)
		{

		}

		CBufferLayout(const std::initializer_list<SBufferElement>& rElements)
			:m_vecElement(rElements) 
		{
			CalculatorOffsetsAndStride();
		}

	public:
		inline BufferElementVec::iterator begin()
		{
			return m_vecElement.begin();
		}

		inline BufferElementVec::iterator end()
		{
			return m_vecElement.end();
		}

		inline const BufferElementVec::const_iterator begin() const
		{
			return m_vecElement.begin();
		}

		inline const BufferElementVec::const_iterator end() const
		{
			return m_vecElement.end();
		}

	public:
		inline const BufferElementVec& GetElements() const
		{
			return m_vecElement;
		}

		inline const uint32_t GetStride() const
		{
			return m_unStride;
		}

	private:
		void CalculatorOffsetsAndStride()
		{
			uint32_t unOffset = 0;
			m_unStride = 0;

			for (auto& element : m_vecElement)
			{
				element.m_unOffset = unOffset;
				unOffset += element.m_unSize;
				m_unStride += element.m_unSize;
			}
		}

	private:
		BufferElementVec m_vecElement;
		uint32_t m_unStride;	///< 步长, 基于不同顶点属性的步长顺序写入计算
	};

	/// <summary>
	/// 顶点缓对象 VBO
	/// </summary>
	class CVertexBuffer
	{
	public:
		virtual ~CVertexBuffer() {}

	public:
		virtual void Bind() const  = 0;

		virtual void Unbind() const  = 0;

		virtual void SetLayout(const CBufferLayout& rLayout) = 0;

		virtual const CBufferLayout& GetLayout() const = 0;

		virtual void SetData(const void* pData, uint32_t unDataSize) = 0;

	public:
		static Ref<CVertexBuffer> Create(float* pVertices, uint32_t size);

		static Ref<CVertexBuffer> Create(uint32_t unSize);
	};

	/// <summary>
	/// 索引缓冲对象 IBO
	/// </summary>
	class CIndexBuffer
	{
	public:
		virtual ~CIndexBuffer() {}

	public:
		virtual void Bind() const  = 0;

		virtual void Unbind() const = 0;

		virtual uint32_t GetCount() const = 0;

	public:
		static CIndexBuffer* Create(uint32_t* pIndices, uint32_t unCount);
	};
}
